home *** CD-ROM | disk | FTP | other *** search
- #import "Group.h"
- #import "GraphicView.h"
- #import <appkit/Application.h>
- #import <appkit/NXImage.h>
- #import <dpsclient/wraps.h>
- #import <objc/List.h>
-
- /* Optimally viewed in a wide window. Make your window big enough so that this comment fits entirely on one line w/o wrapping. */
-
- #define GROUP_CACHE_THRESHOLD 4
-
- @implementation Group : Graphic
- /*
- * This Graphic is used to create heirarchical groups of other Graphics.
- * It simply keeps a list of all the Graphics in the group and resizes
- * and translates them as the Group object itself is resized and moved.
- * It also passes messages sent to the Group onto its members.
- *
- * For efficiency, we cache the group whenever it passes the caching
- * threshold. Thus, grouping becomes a tool to let the user have some
- * control over the memory/speed tradeoff (which can be different
- * depending on the kind of drawing the user is making).
- */
-
- /* Factory method */
-
- - initList:list
- /*
- * Creates a new grouping with list containing the list of Graphics
- * in the group. Groups of Groups is perfectly allowable. We have
- * to keep track of the largest linewidth in the group as well as
- * whether any of the elements of the group have arrows since both
- * of those attributes affect the extended bounds of the Group.
- * We set any objects which might be cacheing (notably subgroups of
- * this group) to be not cacheable since it is no use for them to
- * cache themselves when we are caching them as well.
- */
- {
- id g;
- int i;
- NXRect r;
-
- [super init];
-
- i = [list count];
- g = [list objectAt:--i];
- [g getBounds:&bounds];
- gFlags.arrow = [g lineArrow];
- linewidth = [g lineWidth];
- while (i) {
- g = [list objectAt:--i];
- [g getBounds:&r];
- [g setCacheable:NO];
- if (!r.size.width) r.size.width = 1.0;
- if (!r.size.height) r.size.height = 1.0;
- NXUnionRect(&r, &bounds);
- if (!gFlags.arrow && [g lineArrow]) gFlags.arrow = [g lineArrow];
- if ([g lineWidth] > linewidth) linewidth = [g lineWidth];
- }
-
- components = list;
- lastRect = bounds;
-
- return self;
- }
-
- - free
- {
- [components freeObjects];
- [components free];
- [cache free];
- return [super free];
- }
-
- /* Public methods */
-
- - transferSubGraphicsTo:list at:(int)position
- {
- int i;
-
- i = [components count];
- while (i--) [list insertObject:[[components removeLastObject] setCacheable:YES] at:position];
-
- return self;
- }
-
- /* Methods overridden from superclass */
-
- - setCacheable:(BOOL)flag
- /*
- * Sets whether we do caching of this Group or not.
- */
- {
- dontCache = flag ? NO : YES;
- if (dontCache) {
- [cache free];
- cache = nil;
- }
- return self;
- }
-
- - (BOOL)isCacheable
- {
- return !dontCache;
- }
-
- - draw
- /*
- * Individually scales and translates each Graphic in the group and draws
- * them. This is done this way so that ungrouping is trivial. Note that
- * if we are caching, we need to take the extra step of translating
- * everything to the origin, drawing them in the cache, then translating
- * them back.
- */
- {
- int i;
- Graphic *g;
- NXRect eb, b;
- float sx = 1.0, sy = 1.0, tx, ty;
- BOOL changed, changedSize, caching = NO;
-
- if (bounds.size.width < 1.0 || bounds.size.height < 1.0 || !components) return self;
-
- changedSize = lastRect.size.width != bounds.size.width || lastRect.size.height != bounds.size.height;
- changed = changedSize || lastRect.origin.x != bounds.origin.x || lastRect.origin.y != bounds.origin.y;
-
- if ((changedSize || !cache) && NXDrawingStatus == NX_DRAWING) {
- [cache free];
- cache = nil;
- if (DrawStatus != Resizing && !dontCache && [components count] > GROUP_CACHE_THRESHOLD) {
- caching = YES;
- [self getExtendedBounds:&eb];
- cache = [[NXImage allocFromZone:[self zone]] initSize:&eb.size];
- [cache lockFocus];
- [[[NXApp focusView] window] reenableDisplay]; /* workaround for AppKit bug */
- b = eb; b.origin.x = b.origin.y = 0.0;
- PSsetalpha(0.0);
- PSsetgray(NX_WHITE);
- NXRectFill(&b);
- PSsetalpha(1.0);
- }
- }
-
- if (changedSize) {
- sx = bounds.size.width / lastRect.size.width;
- sy = bounds.size.height / lastRect.size.height;
- }
-
- i = [components count];
- while (i) {
- g = [components objectAt:--i];
- if (changed) {
- [g getBounds:&b];
- tx = (bounds.origin.x + ((b.origin.x - lastRect.origin.x) / lastRect.size.width * bounds.size.width)) - b.origin.x;
- ty = (bounds.origin.y + ((b.origin.y - lastRect.origin.y) / lastRect.size.height * bounds.size.height)) - b.origin.y;
- b.origin.x = b.origin.x + tx;
- b.origin.y = b.origin.y + ty;
- b.size.width = b.size.width * sx;
- b.size.height = b.size.height * sy;
- [g setBounds:&b];
- } else if (caching) {
- [g getBounds:&b];
- }
- if (NXDrawingStatus != NX_DRAWING || !cache || caching) {
- if (caching) {
- b.origin.x -= eb.origin.x;
- b.origin.y -= eb.origin.y;
- [g setBounds:&b];
- }
- [g setGraphicsState]; /* does a gsave ... */
- [g draw];
- PSgrestore(); /* ... so we need this grestore */
- if (caching) {
- b.origin.x += eb.origin.x;
- b.origin.y += eb.origin.y;
- [g setBounds:&b];
- }
- }
- }
-
- if (cache && NXDrawingStatus == NX_DRAWING) {
- if (caching) {
- [cache unlockFocus];
- } else {
- [self getExtendedBounds:&eb];
- }
- [cache composite:NX_SOVER toPoint:&eb.origin];
- }
-
- lastRect = bounds;
-
- return self;
- }
-
- - (BOOL)hit:(const NXPoint *)point
- /*
- * Gets a hit if any of the items in the group gets a hit.
- */
- {
- id g;
- int i;
- NXPoint p;
- float px, py;
-
- if ([super hit:point]) {
- if (components) {
- p = *point;
- px = (p.x - bounds.origin.x) / bounds.size.width;
- p.x = px * lastRect.size.width + lastRect.origin.x;
- py = (p.y - bounds.origin.y) / bounds.size.height;
- p.y = py * lastRect.size.height + lastRect.origin.y;
- i = [components count];
- while (i) {
- g = [components objectAt:--i];
- if ([g hit:&p]) return YES;
- }
- } else {
- return YES;
- }
- }
-
- return NO;
- }
-
- /* Compatibility methods */
-
- - replaceWithImage
- /*
- * Since we got rid of Tiff and PSGraphic and replaced them
- * with the unified Image graphic, we need to go through our
- * list and replace all of them with an Image graphic.
- */
- {
- int i;
- id g, newg;
-
- for (i = [components count]-1; i >= 0; i--) {
- g = [components objectAt:i];
- newg = [g replaceWithImage];
- if (g != newg) {
- if (g) {
- [components replaceObjectAt:i with:newg];
- } else {
- [components removeObjectAt:i];
- }
- }
- }
-
- return self;
- }
-
- /* Group must override all the setting routines to forward to components */
-
- - makeGraphicsPerform:(SEL)aSelector with:(const void *)anArgument
- {
- [components makeObjectsPerform:aSelector with:(id)anArgument];
- [cache free];
- cache = nil;
- return self;
- }
-
- - changeFont:sender
- {
- return [self makeGraphicsPerform:@selector(changeFont:) with:sender];
- }
-
- - font
- {
- int i;
- id gfont, font = nil;
-
- i = [components count];
- while (i--) {
- gfont = [[components objectAt:i] font];
- if (gfont) {
- if (font && font != gfont) {
- font = nil;
- break;
- } else {
- font = gfont;
- }
- }
- }
-
- return font;
- }
-
- - setLineWidth:(const float *)value
- {
- return [self makeGraphicsPerform:@selector(setLineWidth:) with:value];
- }
-
- - setGray:(const float *)value
- {
- return [self makeGraphicsPerform:@selector(setGray:) with:value];
- }
-
- - setFillColor:(NXColor *)aColor
- {
- return [self makeGraphicsPerform:@selector(setFillColor:) with:aColor];
- }
-
- - setFill:(int)mode
- {
- return [self makeGraphicsPerform:@selector(setFill:) with:(void *)mode];
- }
-
- - setLineColor:(NXColor *)aColor
- {
- return [self makeGraphicsPerform:@selector(setLineColor:) with:aColor];
- }
-
- - setLineCap:(int)value
- {
- return [self makeGraphicsPerform:@selector(setLineCap:) with:(void *)value];
- }
-
- - setLineArrow:(int)value
- {
- return [self makeGraphicsPerform:@selector(setLineArrow:) with:(void *)value];
- }
-
- - setLineJoin:(int)value
- {
- return [self makeGraphicsPerform:@selector(setLineJoin:) with:(void *)value];
- }
-
- /* Archiving methods */
-
- - write:(NXTypedStream *)stream
- /*
- * Just writes out the components.
- */
- {
- [super write:stream];
- NXWriteTypes(stream, "@", &components);
- return self;
- }
-
- - read:(NXTypedStream *)stream
- {
- [super read:stream];
- NXReadTypes(stream, "@", &components);
- lastRect = bounds;
- return self;
- }
-
- @end
-
-